﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GN.Pay.Application.GatewayApp.Dto.Input;
using GN.Pay.Application.GatewayApp.Dto.Output;
using AutoMapper;
using Abp.Domain.Uow;
using GN.Pay.Core.Entities;
using GN.Pay.Core.Services;
using GN.Pay.Core.Values.TradeRefund;
using GN.Pay.Application.Dto.Input;
using GN.Pay.Application.Dto;
using Abp.AutoMapper;
using GN.Pay.Core.Repositories;
using Abp.Auditing;
using GN.Pay.Core.Exceptions;
using GN.Pay.TradeChannel;
using GN.Pay.TradeChannel.DefaultImpl;
using Abp.Dependency;
using GN.Pay.Core.Values.Entities;
using GN.Pay.Core.Runtime;
using GN.Pay.Core.Values.Channel;
using GN.Pay.Core.Values.Services;
using GN.Pay.TradeChannel.ProtocolPay;
using GN.Pay.Core.Values.TradeTransfers;
using GN.Pay.Core.Tasks.Queue;

namespace GN.Pay.Application.GatewayApp.Impl
{
    /// <summary>
    /// 交易申请服务
    /// </summary>
    public class TradeApplyAppService : GatewayAppService, ITradeApplyAppService
    {
        private readonly ITradePayApplyRepository repository;
        private readonly ITradeAccountBookRepository bookRepository;
        private readonly ITradeCacheService tradeCacheService;
        private readonly ITradePayApplyChannelRepository tradePayApplyChannelRepository;
        private readonly IIocManager iocManager;
        private readonly IQueueManager queueManager;

        public IClientInfoProvider ClientInfo { get; set; }

        public TradeApplyAppService(ITradePayApplyRepository repository,
            ITradeAccountBookRepository bookRepository,
            ITradeCacheService tradeCacheService,
            ITradePayApplyChannelRepository tradePayApplyChannelRepository,
            IQueueManager queueManager,
            IIocManager iocManager)
        {
            this.repository = repository;
            this.bookRepository = bookRepository;
            this.tradeCacheService = tradeCacheService;
            this.tradePayApplyChannelRepository = tradePayApplyChannelRepository;
            ClientInfo = Abp.Auditing.NullClientInfoProvider.Instance;
            this.iocManager = iocManager;
            this.queueManager = queueManager;
        }

        /// <summary>
        /// 收银台付款申请
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public CashiePayApplyOutput CashiePayApply(CashiePayApplyInput input)
        {
            var apply = this.repository.Get(input.ApplyId);
            if (apply == null)
            {
                return null;
            }
            if (apply.IsExpire())
            {
                PayExceptionUtils.ThrowPayValidationException("订单支付已过期，请重新生成订单。");
            }
            var appInfo = tradeCacheService.GetMerchantAppInfo(apply.AppId);
            if (appInfo == null)
            {
                return null;
            }
            var result = apply.MapTo<CashiePayApplyOutput>();
            result.ApplyId = apply.Id;
            result.AppName = appInfo.AppName;
            result.CashierName = appInfo.CashierName;
            result.MerchantName = appInfo.MerchantName;
            result.MerchantType = appInfo.MerchantType;
            result.LogoUrl = appInfo.LogoUrl;
            if (input.ChannelType != ChannelType.None)
            {
                var channels = tradeCacheService.GetTradeChannels(apply.AppId);
                result.Channels = channels.Where(s => (s.ChannelType & input.ChannelType) != 0)
                    .Select(s => s.ChannelId).ToList();
            }
            LoadChannelCategory(result);
            return result;
        }

        private void LoadChannelCategory(CashiePayApplyOutput result)
        {
            if (result.IsShowProtocolPay())
            {
                IBankCardAppService bankService = iocManager.Resolve<IBankCardAppService>();
                result.BankCardList = bankService.MerchantMemberBankCardList(result.ApplyId);
            }
            if (result.IsShowNetWorkBankPay())
            {
                if (result.IsSupportChannel("Gyb.WebPay"))
                {
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Gyb.WebPay", ChannelName = "贵阳银行", Style = "GYDB", StyleFor = "GyccbWebPay" });
                }
                if (result.IsSupportChannel("Union.WebPay"))
                {
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "中国农业银行", Style = "ABC", StyleFor = "ABC" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "中国交通银行", Style = "BCOM", StyleFor = "BCOM" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "中国建设银行", Style = "CCB", StyleFor = "CCB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "中国工商银行", Style = "ICBC", StyleFor = "ICBC" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "中国银行", Style = "BOC", StyleFor = "BOC" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "中国光大银行", Style = "CEB", StyleFor = "CEB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "招商银行", Style = "CMB", StyleFor = "CMB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "中国民生银行", Style = "CMBC", StyleFor = "CMBC" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "兴业银行", Style = "CIB", StyleFor = "CIB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "中信银行", Style = "CITIC", StyleFor = "CITIC" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "平安银行", Style = "PAB", StyleFor = "PAB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "中国邮政储蓄银行", Style = "PSBC", StyleFor = "PSBC" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "东亚银行", Style = "BEA", StyleFor = "BEA" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "北京银行", Style = "BOB", StyleFor = "BOB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "渤海银行", Style = "CBHB", StyleFor = "CBHB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "浙商银行", Style = "CZB", StyleFor = "CZB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "广发银行", Style = "GDB", StyleFor = "GDB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "宁波银行", Style = "NBCB", StyleFor = "NBCB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "南京银行", Style = "NJCB", StyleFor = "NJCB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "深圳发展银行", Style = "SDB", StyleFor = "SDB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "上海银行", Style = "SHB", StyleFor = "SHB" });
                    result.NetWorkBankChannels.Add(new CashiePayChannelOutput() { ChannelId = "Union.WebPay", ChannelName = "浦发银行", Style = "SPDB", StyleFor = "SPDB" });
                }
            }
            if (result.IsShowThirdPartyPay())
            {
                if (result.IsSupportChannel("AliPay.Transfer"))
                {
                    result.ThirdPartyChannels.Add(new CashiePayChannelOutput() { ChannelId = "AliPay.Transfer", ChannelName = "支付宝", Style = "ZFBD", StyleFor = "AliPayTransfer" });
                }
                if (result.IsSupportChannel("WeiXin.QRCode"))
                {
                    result.ThirdPartyChannels.Add(new CashiePayChannelOutput() { ChannelId = "WeiXin.QRCode", ChannelName = "微信", Style = "WXBD", StyleFor = "WeiXinQRCode" });
                }
                if (result.IsSupportChannel("YJPay.WebPay"))
                {
                    result.ThirdPartyChannels.Add(new CashiePayChannelOutput() { ChannelId = "YJPay.WebPay", ChannelName = "易极付", Style = "YJBD", StyleFor = "YJPayWebPay" });
                }
            }
        }

        /// <summary>
        /// 检查是否支付
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="orderNo"></param>
        private void CheckIsPay(string appId, string orderNo)
        {
            var bookOrder = this.bookRepository.GetAll().FirstOrDefault(a => a.AppId == appId && a.OrderNo == orderNo);
            if (bookOrder != null)
            {
                PayExceptionUtils.ThrowPayValidationException("订单不能重复支付");
            }
        }

        private ITradeChannel CheckTradeChannel(string appId, string channelId)
        {
            ITradeChannel channel = tradeCacheService.GetTradeChannel(appId, channelId);
            if (channel == null)
            {
                PayExceptionUtils.ThrowPayValidationException("不支持或未配置支付方式 " + channelId);
            }
            return channel;
        }

        private TradePayApply _PayApplyRegister(PayApplyInput input)
        {
            var apply = input.MapTo<TradePayApply>();
            apply.Check();
            this.CheckIsPay(apply.AppId, apply.OrderNo);
            var oldApply = this.repository.FirstOrDefault(o => o.AppId == apply.AppId && o.OrderNo == apply.OrderNo);
            if (oldApply != null)
            {
                if (oldApply.OrderMoney != apply.OrderMoney)
                {
                    PayExceptionUtils.ThrowPayValidationException("同一订单号多次请求时，订单金额必须一致");
                }
                return oldApply;
            }
            else
            {
                var app = this.tradeCacheService.GetMerchantAppInfo(apply.AppId);
                if (app == null)
                {
                    PayExceptionUtils.ThrowPayValidationException("应用不存在或已被限制访问");
                }
                apply.Id = Guid.NewGuid().ToString("N");
                apply.ApplyPlatformCount = 0;
                apply.ApplyTime = DateTime.Now;
                apply.AppIp = ClientInfo.ClientIpAddress == null ? "" : ClientInfo.ClientIpAddress;
                if (apply.ExpireMinute <= 0)
                {
                    apply.ExpireMinute = 120;
                }
                apply.NotifyUrl = string.IsNullOrWhiteSpace(apply.NotifyUrl) ? app.PayDefaultNotifyUrl : apply.NotifyUrl;
                apply.ReturnUrl = string.IsNullOrWhiteSpace(apply.ReturnUrl) ? app.PayDefaultReturnUrl : apply.ReturnUrl;
                this.repository.Insert(apply);
                repository.SaveChanges();
                try
                {
                    var msg = new RabbitMQ.GNClient.ObjectMessage(apply.Id);
                    queueManager.Publish(QueueType.PayApplyExpire, msg, new TimeSpan(0, apply.ExpireMinute, 0));
                }
                catch (Exception err)
                {
                    this.Logger.Error("付款申请过期进入队列出错!", err);
                }
                return apply;
            }
        }

        /// <summary>
        /// 付款申请注册
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [UnitOfWork(System.Transactions.IsolationLevel.RepeatableRead)]
        public PayApplyOutput PayApplyRegister(PayApplyInput input)
        {
            var apply = _PayApplyRegister(input);
            var result = apply.MapTo<PayApplyOutput>();
            result.ApplyId = apply.Id;
            return result;
        }

        /// <summary>
        /// 第三方通道的订单号
        /// </summary>
        /// <param name="orderNo">订单号</param>
        /// <returns>返回第三方通道的订单号</returns>
        private string ChannelOrderNo(string orderNo)
        {
            return DateTime.Now.ToString("yyyyMMddHHmmssffffff");

            //贵阳银行最长为20
            // return channelNumberSuffix + "_" + orderNo + "_" + appNumberSuffix;
        }

        private IOrderPayApply CreateOrderPayApply(ITradeChannel channel, MerchantAppCahceInfo app,
            TradePayApply apply, TradePayApplyChannel channelApply, PayChannelInput input)
        {
            if (channel.IsProtocolPay())
            {
                if (string.IsNullOrWhiteSpace(apply.MemberNo))
                {
                    PayExceptionUtils.ThrowPayValidationException("协议支付时，需要指定会员或会员需登录。");
                }
                if (string.IsNullOrWhiteSpace(input.BankCradPayId))
                {
                    PayExceptionUtils.ThrowPayValidationException("协议支付时，需要选择支付银行卡。");
                }
                long bankCradId;
                if (!long.TryParse(input.BankCradPayId.Trim(), out bankCradId))
                {
                    PayExceptionUtils.ThrowPayValidationException("协议支付时，需要选择有效的支付银行卡。");
                }
                var memberRepository = iocManager.Resolve<IMerchantMemberRepository>();
                var bankCardRepository = iocManager.Resolve<IMerchantMemberBankCardRepository>();

                var item = (from q in bankCardRepository.GetAll()
                            join p in memberRepository.GetAll() on q.MemberId equals p.Id
                            where p.MemberNo == apply.MemberNo && q.Id == bankCradId && p.MerchantId == app.MerchantId
                            select q).FirstOrDefault();

                if (item == null)
                {
                    PayExceptionUtils.ThrowPayValidationException("协议支付时，需要选择已绑定的支付银行卡。");
                }
                return new OrderProtocolPayApply()
                {
                    Body = string.IsNullOrWhiteSpace(apply.Body) ? "无" : apply.Body,
                    ClientIP = channelApply.ClientIP,
                    StartTime = apply.ApplyTime,
                    ExpireTime = apply.ApplyTime.AddMinutes(apply.ExpireMinute),
                    OrderMoney = apply.OrderMoney,
                    Subject = string.IsNullOrWhiteSpace(apply.Subject) ? "无" : apply.Subject,
                    OrderNo = channelApply.Id,
                    ExtraParam = app.Id + "_" + apply.OrderNo,
                    ChannelParams = input.ChannelParams,
                    AccountName = item.AccountName,
                    AccountNo = item.AccountNo,
                    AccountType = item.AccountType == 1 ? BankAccountType.Debit : BankAccountType.Credit,
                    BankNo = item.BankNo,
                    CreditCardLastNo = item.CreditCardLastNo,
                    CreditCardLastTerm = item.CreditCardLastTerm,
                    IdentityNo = item.IdentityNo,
                    MemberId = item.MemberId,
                    MobilePhone = item.MobilePhone,
                    OpenBankCity = item.OpenBankCity,
                    OpenBankName = item.OpenBankName,
                    ProtocolNo = item.ProtocolNo,
                    UserToken = apply.UserToken
                };
            }
            else
            {
                return new OrderPayApply()
                {
                    Body = string.IsNullOrWhiteSpace(apply.Body) ? "无" : apply.Body,
                    ClientIP = channelApply.ClientIP,
                    StartTime = apply.ApplyTime,
                    ExpireTime = apply.ApplyTime.AddMinutes(apply.ExpireMinute),
                    OrderMoney = apply.OrderMoney,
                    Subject = string.IsNullOrWhiteSpace(apply.Subject) ? "无" : apply.Subject,
                    OrderNo = channelApply.Id,
                    ExtraParam = app.Id + "_" + apply.OrderNo,
                    ChannelParams = input.ChannelParams,
                    UserToken = apply.UserToken
                };
            }
        }

        private PayChannelOutput _PayChannelRegister(TradePayApply apply, PayChannelInput input)
        {
            var tempChannelApply = input.MapTo<TradePayApplyChannel>();
            if (apply == null)
            {
                if (bookRepository.Count(s => s.ApplyId == tempChannelApply.ApplyId) > 0)
                {
                    PayExceptionUtils.ThrowPayValidationException("订单已支付,不能重复支付。");
                }
                PayExceptionUtils.ThrowPayValidationException("指定的申请未注册，不能直接申请支付");
            }
            if (apply.ApplyTime.AddMinutes(apply.ExpireMinute) < DateTime.Now)
            {
                PayExceptionUtils.ThrowPayValidationException("订单支付已过期，请重新生成订单。");
            }
            var app = this.tradeCacheService.GetMerchantAppInfo(apply.AppId);
            if (app == null)
            {
                PayExceptionUtils.ThrowPayValidationException("应用不存在");
            }
            ITradeChannel channel = this.CheckTradeChannel(apply.AppId, tempChannelApply.ChannelId);
            if (channel.IsProtocolPay() && string.IsNullOrWhiteSpace(input.BankCradPayId))
            {
                PayExceptionUtils.ThrowPayValidationException("协议支付时，需要指定支付银行卡");
            }
            this.CheckIsPay(apply.AppId, apply.OrderNo);
            var channelApply = this.tradePayApplyChannelRepository
                 .FirstOrDefault(s => s.ApplyId == apply.Id && s.ChannelId == tempChannelApply.ChannelId);
            bool isInsert = false;
            if (channelApply == null)
            {
                isInsert = true;
                channelApply = new TradePayApplyChannel()
                {
                    Id = ChannelOrderNo(apply.OrderNo),
                    ApplyId = apply.Id,
                    ChannelId = channel.ChannelId,
                    ClientIP = tempChannelApply.ClientIP
                };
            }
            else
            {
                if (channelApply.CallType == (int)TradePayApplyResultCallType.PaySuccess)
                {
                    PayExceptionUtils.ThrowPayValidationException("正在支付中，不能重复支付.");
                }
                if (!channel.IsPayNewRequest)
                {
                    return new PayChannelOutput()
                    {
                        ApplyId = apply.Id,
                        CallType = channelApply.CallType,
                        ChannelId = tempChannelApply.ChannelId,
                        ClientIP = tempChannelApply.ClientIP,
                        ExpireTime = apply.ApplyTime.AddMinutes(apply.ExpireMinute),
                        StartTime = apply.ApplyTime,
                        PayId = channelApply.PayId,
                        PayUrl = channelApply.PayUrl,
                        PayParams = channelApply.PayParams,
                        TradeNo = channelApply.Id
                    };
                }
            }
            bool isUpdate;
            var payOrder = this.CreateOrderPayApply(channel, app, apply, channelApply, input);
            var result = PayApply(channel, app, apply, payOrder, input, tempChannelApply.ClientIP, out isUpdate);
            if (isInsert && isUpdate)
            {
                channelApply.CallType = (int)result.CallType;
                channelApply.PayId = result.PayId;
                channelApply.PayUrl = result.PayUrl;
                channelApply.PayParams = result.PayParam;
                this.tradePayApplyChannelRepository.Insert(channelApply);
            }
            return CreatePayChannelOutput(result, channelApply, payOrder);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="app"></param>
        /// <param name="apply"></param>
        /// <param name="order"></param>
        /// <param name="input"></param>
        /// <param name="clientIp"></param>
        /// <param name="isUpdate"></param>
        /// <returns></returns>
        private ITradePayApplyResult PayApply(ITradeChannel channel, MerchantAppCahceInfo app, TradePayApply apply,
            IOrderPayApply order, PayChannelInput input, string clientIp, out bool isUpdate)
        {
            if (channel.IsProtocolPay())
            {
                isUpdate = false;
                if (!(order is IProtocolPayApply))
                {
                    PayExceptionUtils.ThrowPayValidationException("协议支付时，系统出错，无协议支付订单信息。");
                }
                var smsTemplate = app.SmsTemplates.FirstOrDefault(s => s.SmsType == 101);
                if (smsTemplate == null)
                {
                    PayExceptionUtils.ThrowPayValidationException("无法进行付款申请，未指定配置短信模板。");
                }
                var payId = Guid.NewGuid().ToString("N");
                var generateIdentifier = iocManager.Resolve<IGenerateIdentifier>();
                var smsCheckCode = generateIdentifier.GenerateSmsCheckCode();
                var oppa = order as OrderProtocolPayApply;
                var pro = new ProtocolPaySmsCheck()
                {
                    AppId = app.Id,
                    ApplyId = apply.Id,
                    ClientOrderNo = apply.OrderNo,
                    MerchantId = app.MerchantId,
                    Order = oppa,
                    ClientIP = clientIp,
                    ChannelId = channel.ChannelId,
                    SmsCheckCodeHash = generateIdentifier.GeneratePayApplyHashSmsCheckCode(apply.Id, app, order, smsCheckCode),
                    SmsExpireMinute = 5
                };
                tradeCacheService.SetProtocolPaySmsCheck(payId, pro);
                var content = new ShortcutPaySmsContent()
                {
                    AppName = app.AppName,
                    CardNo = oppa.AccountNo,
                    CheckCode = smsCheckCode,
                    ExpireMinute = pro.SmsExpireMinute,
                    MobilePhone = oppa.MobilePhone,
                    RealName = oppa.AccountName,
                    SmsTemplate = smsTemplate.SmsTemplate,
                    OrderNo = apply.OrderNo,
                    PayMoney = oppa.OrderMoney
                };
                var smsService = iocManager.Resolve<IPaySmsService>();
                smsService.ShortcutPay(content);
                return new TradePayApplyResult(payId, "", "", TradePayApplyResultCallType.Wait_SmsCheck);
            }
            else
            {
                isUpdate = true;
                return channel.PayApply(order);
            }
        }

        [UnitOfWork(System.Transactions.IsolationLevel.RepeatableRead)]
        public PayChannelOutput PayChannelRegister(PayChannelInput input)
        {
            var apply = repository.Get(input.ApplyId);
            return _PayChannelRegister(apply, input);
        }

        [UnitOfWork(System.Transactions.IsolationLevel.RepeatableRead)]
        public PayChannelOutput PayApplyAndChannelRegister(PayApplyAndChannelInput input)
        {
            var apply = _PayApplyRegister(input);
            return _PayChannelRegister(apply, new PayChannelInput()
            {
                AppId = input.AppId,
                ApplyId = apply.Id,
                ChannelId = input.ChannelId,
                ClientIP = input.ClientIP,
                MerchantId = input.MerchantId,
                ChannelParams = input.ChannelParams,
                BankCradPayId = input.BankCradPayId
            });
        }

        /// <summary>
        /// 退款申请
        /// </summary>
        /// <param name="input">输入</param>
        /// <returns></returns>
        [UnitOfWork(System.Transactions.IsolationLevel.RepeatableRead)]
        public Core.Values.TradeRefund.TradeRefundApplyResult RefundApply(RefundApplyInput input)
        {
            var service = iocManager.Resolve<ITradeRefundApplyService>();
            return service.RefundApply(input.MapTo<TradeRefundApplyInfo>());
        }

        /// <summary>
        /// 转账申请
        /// </summary>
        /// <param name="input">输入</param>
        /// <returns></returns>
        [UnitOfWork(System.Transactions.IsolationLevel.RepeatableRead)]
        public TradeTransfersApplyResult TransfersApply(TransfersApplyInput input)
        {
            var service = iocManager.Resolve<ITradeTransfersApplyService>();
            return service.TransfersApply(input.MapTo<TradeTransfersApplyInfo>());
        }

        /// <summary>
        /// 短信付款
        /// </summary>
        /// <param name="input">输入</param>
        /// <returns></returns>
        [UnitOfWork(false)]
        public PayChannelOutput SmsPay(SmsPayInput input)
        {
            var op = this.CreateUpdateUnitOfWorkOptions();
            using (var uow = this.UnitOfWorkManager.Begin(op))
            {
                var pro = tradeCacheService.GetProtocolPaySmsCheck(input.PayId);
                if (pro == null)
                {
                    PayExceptionUtils.ThrowPayValidationException("支过已过期或失效。");
                }
                var app = this.tradeCacheService.GetMerchantAppInfo(pro.AppId);
                if (app == null)
                {
                    PayExceptionUtils.ThrowPayValidationException("应用不存在或已下架。");
                }
                var generateIdentifier = iocManager.Resolve<IGenerateIdentifier>();
                var smsCheckCodeHash = generateIdentifier.GeneratePayApplyHashSmsCheckCode(pro.ApplyId, app, pro.Order, input.SmsCheckCode);
                if (!smsCheckCodeHash.Equals(pro.SmsCheckCodeHash))
                {
                    PayExceptionUtils.ThrowPayValidationException("短信验证码不正确。");
                }
                CheckIsPay(pro.AppId, pro.ClientOrderNo);
                ITradeChannel channel = this.CheckTradeChannel(pro.AppId, pro.ChannelId);
                if (!channel.IsProtocolPay())
                {
                    PayExceptionUtils.ThrowPaySystemException("系统出错，支付方式不支持协议支付。");
                }
                var channelApply = this.tradePayApplyChannelRepository
                    .FirstOrDefault(s => s.ApplyId == pro.ApplyId && s.ChannelId == pro.ChannelId);
                if (channelApply == null)
                {
                    var result = channel.PayApply(pro.Order);
                    if (result.CallType == TradePayApplyResultCallType.PaySuccess)
                    {
                        if (result.PayResult == null)
                        {
                            PayExceptionUtils.ThrowPaySystemException("系统出错，无法获取支付结果。");
                        }
                    }
                    channelApply = new TradePayApplyChannel()
                    {
                        Id = pro.Order.OrderNo,
                        ApplyId = pro.ApplyId,
                        ChannelId = channel.ChannelId,
                        ClientIP = pro.ClientIP,
                        CallType = (int)result.CallType,
                        PayId = result.PayId,
                        PayParams = result.PayParam,
                        PayUrl = result.PayUrl
                    };
                    this.tradePayApplyChannelRepository.Insert(channelApply);
                    this.tradePayApplyChannelRepository.SaveChanges();
                    var smsPayResult = CreatePayChannelOutput(result, channelApply, pro.Order);
                    uow.Complete();
                    if (result.CallType == TradePayApplyResultCallType.PaySuccess)
                    {
                        var notifyService = iocManager.Resolve<ITradeNotifyService>();
                        notifyService.PayNotify(result.PayResult, new TimeSpan(0, 0, 2));
                        tradeCacheService.RemoveProtocolPaySmsCheck(input.PayId);
                    }
                    return smsPayResult;
                }
                else
                {
                    throw PayExceptionUtils.ThrowPayValidationException("正在支付中，不能重复支付.");
                }
            }
        }

        private PayChannelOutput CreatePayChannelOutput(ITradePayApplyResult result,
            TradePayApplyChannel channel, IOrderPayApply payApply)
        {
            return new PayChannelOutput()
            {
                ApplyId = channel.ApplyId,
                CallType = (int)result.CallType,
                ChannelId = channel.ChannelId,
                ClientIP = channel.ClientIP,
                ExpireTime = payApply.ExpireTime,
                StartTime = payApply.StartTime,
                PayId = result.PayId,
                PayUrl = result.PayUrl,
                PayParams = result.PayParam,
                TradeNo = channel.Id
            };
        }


    }
}
